﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DotNetNuke.Framework.Providers;
using DotNetNuke.Security.Membership;
using DotNetNuke.Framework;
using System.Web.Configuration;
using System.Web.Compilation;
using DotNetNuke.Entities.Users;
using System.Globalization;
using System.Security;
using System.Runtime.InteropServices;

namespace BrandonHaynes.Membership
	{
	/// <summary>
	/// An arbitrary batch of extension methods that (hopefully) make the primary code paths more readable.  Nothing
	/// too sophisticated here.
	/// </summary>
	static class Extensions
		{
		/// <summary>
		/// Executes an action against each element in an enumeration (why isn't this in System.Linq?)
		/// </summary>
		public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> action)
			{
			foreach (T t in sequence)
				action(t);
			}

		/// <summary>
		/// Converts a singleton into an enumeration (why isn't this in System.Linq?)
		/// </summary>
		public static IEnumerable<T> ToEnumerable<T>(this T element)
			{ return Enumerable.Repeat(element, 1); }

		/// <summary>
		/// Returns true when the given enumeration is empty; otherwise false
		/// </summary>
		public static bool IsEmpty<T>(this IEnumerable<T> enumeration)
			{ return !enumeration.Any(); }

		/// <summary>
		/// Given a dictionary, returns the value at key if it exists; otherwise returns default(TValue)
		/// </summary>
		public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
		{ return dictionary.GetValueOrDefault(key, default(TValue)); }

		/// <summary>
		/// Given a dictionary, returns the value at a key if it exists; otherwise returns defaultValue
		/// </summary>
		public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
			{
			TValue value;
			return dictionary.TryGetValue(key, out value) ? value : defaultValue;
			}

		/// <summary>
		/// Given a DotNetNuke provider configuration, returns any provider that matches the given type
		/// </summary>
		public static Provider GetByType(this ProviderConfiguration providerConfiguration, Type type)
			{
			return providerConfiguration.Providers.Values.Cast<Provider>().
				Single(provider => provider.Type.StartsWith(type.FullName, StringComparison.OrdinalIgnoreCase));
			}

		/// <summary>
		/// Returns the remainder of a string occuring after the token afterValue
		/// </summary>
		/// <param name="value">The string to search</param>
		/// <param name="afterValue">The value to search for</param>
		/// <returns>The portion of value remaining after the token afterValue is found</returns>
		public static string SubstringAfter(this string value, string afterValue)
			{
			int index = value.LastIndexOf(afterValue, StringComparison.OrdinalIgnoreCase);
			return index >= 0 ? value.Substring(index + afterValue.Length) : null;
			}

		/// <summary>
		/// Returns a property value of a user, either from the user itself or a user's profile.
		/// 
		/// Supported tokens include {userName, userId, email, affiliateId, displayName, firstName, lastIpAddress,
		/// portalId, or any profile property name }.
		/// </summary>
		/// <param name="user">The user to query</param>
		/// <param name="property">One of the property names of the UserInfo object, or a profile property name</param>
		/// <returns></returns>
		public static string GetUserProperty(this UserInfo user, string property)
			{
			switch (property.ToUpperInvariant())
				{
				case "USERNAME":
					return user.Username;
				case "USERID":
					return user.UserID.ToString(CultureInfo.InvariantCulture);
				case "EMAIL":
					return user.Email;
				case "AFFILIATEID":
					return user.AffiliateID.ToString(CultureInfo.InvariantCulture);
				case "DISPLAYNAME":
					return user.DisplayName;
				case "FIRSTNAME":
					return user.FirstName;
				case "LASTIPADDRESS":
				case "IPADDRESS":
					return user.LastIPAddress;
				case "LASTNAME":
					return user.LastName;
				case "PORTALID":
					return user.PortalID.ToString(CultureInfo.InvariantCulture);
				default:
					return user.Profile.GetPropertyValue(property);
				}
			}

		/// <summary>
		/// Given a character array, converts to a SecureString
		/// </summary>
		public static SecureString ToSecureString(ref char[] value)
			{
			var secureString = new SecureString();
			value.ForEach(character => secureString.AppendChar(character));
			return secureString;
			}

		/// <summary>
		/// Given a string, converts to a SecureString
		/// </summary>
		public static SecureString ToSecureString(ref string value)
			{
			var secureString = new SecureString();

			foreach (char character in value)
				secureString.AppendChar(character);

			return secureString;
			}

		/// <summary>
		/// Given a string, converts to a read-only SecureString
		/// </summary>
		public static SecureString ToReadOnlySecureString(string value)
			{
			var secureString = ToSecureString(ref value);
			secureString.MakeReadOnly();

			return secureString;
			}
		}
	}
